//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <string.h>
#include <ctype.h>

#include "_sprintf.h"

// FROM: lx bios 1.1

#define FMT_RJUST   0
#define FMT_LJUST   1
#define FMT_RJUST0  2
#define FMT_CENTER  3

INLINE void str2upper (char *p);
INLINE char * btoa (char * dst, unsigned int value, int base);
INLINE char * _getbase (char *p, int * basep);
INLINE int _atob (unsigned long *vp,char * p, int base);
INLINE int atob (unsigned int   *vp, char * p, int base);
INLINE char *strichr (char *p, int c);
INLINE void str_fmt (char * p, int size, int fmt);

INLINE void str2upper(char *p)
{
    if (!p)
        return;
    for (; *p; p++)
        *p = toupper (*p);
    return;
}

INLINE void str_fmt(char * p, int size, int fmt)
{
    int             n, m, len;

    len = strlen (p);
    switch (fmt) {
        case FMT_RJUST:
            for (n = size - len; n > 0; n--)
                strichr (p, ' ');
            break;
        case FMT_LJUST:
            for (m = size - len; m > 0; m--)
                strcat (p, " ");
            break;
        case FMT_RJUST0:
            for (n = size - len; n > 0; n--)
                strichr (p, '0');
            break;
        case FMT_CENTER:
            m = (size - len) / 2;
            n = size - (len + m);
            for (; m > 0; m--)
                strcat (p, " ");
            for (; n > 0; n--)
                strichr (p, ' ');
            break;
    }
}

INLINE char *strichr(char *p, int c)
{
    char           *t;

    if (!p)
        return (p);
    for (t = p; *t; t++);
    for (; t >= p; t--)
        *(t + 1) = *t;
    *p = c;
    return (p);
}

INLINE char * _getbase(char *p, int * basep)
{
    if (p[0] == '0') {
        switch (p[1]) {
            case 'x':
                *basep = 16;
                break;
            case 't':
            case 'n':
                *basep = 10;
                break;
            case 'o':
                *basep = 8;
                break;
            default:
                *basep = 10;
                return (p);
        }
        return (p + 2);
    }
    *basep = 10;
    return (p);
}

INLINE int _atob(unsigned long *vp, char * p, int base)
{
    unsigned long  value, v1, v2;
    char           *q, tmp[20];
    int             digit;

    if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
        base = 16;
        p += 2;
    }
    if (base == 16 && (q = strchr (p, '.')) != 0) {
        if (q - p > (offset_t)sizeof(tmp) - 1)
            return (0);
        strncpy (tmp, p, q - p);
        tmp[q - p] = '\0';
        if (!_atob (&v1, tmp, 16))
            return (0);
        q++;
        if (strchr (q, '.'))
            return (0);
        if (!_atob (&v2, q, 16))
            return (0);
        *vp = (v1 << 16) + v2;
        return (1);
    }
    value = *vp = 0;
    for (; *p; p++) {
        value *= base;
        if (*p >= '0' && *p <= '9')
            digit = *p - '0';
        else if (*p >= 'a' && *p <= 'f')
            digit = *p - 'a' + 10;
        else if (*p >= 'A' && *p <= 'F')
            digit = *p - 'A' + 10;
        else
            return (0);
        if (digit >= base)
            return (0);
        value += digit;
    }
    *vp = value;
    return (1);
}

INLINE int atob(unsigned int *vp, char * p, int base)
{
    unsigned long  v;
    if (base == 0)
        p = _getbase (p, &base);
    if (_atob (&v, p, base)) {
        *vp = v;
        return (1);
    }
    return (0);
}

INLINE char * btoa(char * dst, unsigned int value, int base)
{
    char            buf[34], digit=0;
    int             i, j, rem, neg;

    if (value == 0) {
        dst[0] = '0';
        dst[1] = 0;
        return (dst);
    }
    neg = 0;
    if (base == -10) {
        base = 10;
        if (value & (1UL << 31)) {
            value = (~value) + 1;
            neg = 1;
        }
    }
    for (i = 0; value != 0; i++) {
        rem = value % base;
        value /= base;
        if (rem >= 0 && rem <= 9)
            digit = rem + '0';
        else if (rem >= 10 && rem <= 36)
            digit = (rem - 10) + 'a';
        buf[i] = digit;
    }
    buf[i] = 0;
    if (neg)
        strcat (buf, "-");

/* reverse the string */
    for (i = 0, j = strlen (buf) - 1; j >= 0; i++, j--)
        dst[i] = buf[j];
    dst[i] = 0;
    return (dst);
}

void miscpy(char *d, wchar_t *s)
{
    while (*s) {
        *d++ = (char)(*s++);
    }
    *d = 0;
}

EXTERN_C int CDECL _vsprintf(char *d, const char *s, va_list ap)
{
    const char     *t;
    char           *p, *dst, tmp[40];
    wchar_t        *wp;
    unsigned int    n;
    int             fmt, trunc, haddot, width,longlong,base=0;

    dst = d;
    *d = 0;
    for (; *s;) {
        if (*s == '%') {
            s++;
            fmt = FMT_RJUST;
            width = trunc = haddot = longlong = 0;
            for (; *s; s++) {
                if (strchr ("dobxXulsScefg%", *s))
                    break;
                else if (*s == '-')
                    fmt = FMT_LJUST;
                else if (*s == '0')
                    fmt = FMT_RJUST0;
                else if (*s == '~')
                    fmt = FMT_CENTER;
                else if (*s == '*') {
                    if (haddot)
                        trunc = va_arg(ap, int);
                    else
                        width = va_arg (ap, int);
                }
                else if (*s >= '1' && *s <= '9') {
                    for (t = s; isdigit (*s); s++);
                    strncpy (tmp, t, s - t);
                    tmp[s - t] = '\0';
                    atob (&n, tmp, 10);
                    if (haddot)
                        trunc = n;
                    else
                        width = n;
                    s--;
                }
                else if (*s == '.')
                    haddot = 1;
            }
            if (*s == '%') {
                *d++ = '%';
                *d = 0;
            }
            else if (*s == 's') {
                p = va_arg (ap, char *);

                if (p)
                    strcpy (d, p);
                else
                    strcpy (d, "(null)");
            }
            else if (*s == 'S') {
                wp = va_arg (ap, wchar_t *);

                if (wp) {
                    miscpy (d, wp);
                }
                else
                    strcpy (d, "(null)");
            }
            else if (*s == 'c') {
                n = va_arg (ap, int);

                *d = n;
                d[1] = 0;
            }
            else {
                if (*s == 'l') {
                    if (*++s == 'l') {
                        longlong = 1;
                        ++s;
                    }
                }
                if (strchr ("dobxXu", *s)) {
                    if (*s == 'd')
                        base = -10;
                    else if (*s == 'u')
                        base = 10;
                    else if (*s == 'x' || *s == 'X')
                        base = 16;
                    else if (*s == 'o')
                        base = 8;
                    else if (*s == 'b')
                        base = 2;
                    btoa (d, va_arg(ap, int), base);

                    if (*s == 'X')
                        str2upper (d);
                }
            }
            if (trunc)
                d[trunc] = 0;
            if (width)
                str_fmt (d, width, fmt);
            for (; *d; d++);
            if (*s)
                s++;
        }
        else
            *d++ = *s++;
    }
    *d = 0;
    return (d - dst);
}

EXTERN_C int CDECL _sprintf(char *buf, const char *fmt,...)
{
    va_list ap;

    va_start(ap, fmt);

    uint_t uLength = _vsprintf(buf, fmt, ap);

    va_end(ap);

    return uLength;
}
